﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.ETL
{
    /// <summary>
    /// 
    /// </summary>
    [Serializable]
    public class FuzzyLinkInstruction:InstructionBase
    {
        #region props
        private DataProviderType _SrcDbProvider1;
        /// <summary></summary>
        public DataProviderType SrcDbProvider1
        {
            get
            {
                return this._SrcDbProvider1;
            }
            set
            {
                this._SrcDbProvider1 = value;
            }
        }

        private DataProviderType _SrcDbProvider2;
        /// <summary></summary>
        public DataProviderType SrcDbProvider2
        {
            get
            {
                return this._SrcDbProvider2;
            }
            set
            {
                this._SrcDbProvider2 = value;
            }
        }

        private DataProviderType _TgtDbProvider;
        /// <summary></summary>
        public DataProviderType TgtDbProvider
        {
            get
            {
                return this._TgtDbProvider;
            }
            set
            {
                this._TgtDbProvider = value;
            }
        }

        private string _SrcConnStr1;
        /// <summary></summary>
        public string SrcConnStr1
        {
            get
            {
                return this._SrcConnStr1;
            }
            set
            {
                this._SrcConnStr1 = value;
            }
        }
        private string _SrcConnStr2;
        /// <summary></summary>
        public string SrcConnStr2
        {
            get
            {
                return this._SrcConnStr2;
            }
            set
            {
                this._SrcConnStr2 = value;
            }
        }

        private string _TgtConnStr;
        /// <summary></summary>
        public string TgtConnStr
        {
            get
            {
                return this._TgtConnStr;
            }
            set
            {
                this._TgtConnStr = value;
            }
        }

        private string _SrcSql1;
        /// <summary></summary>
        public string SrcSql1
        {
            get
            {
                return this._SrcSql1;
            }
            set
            {
                this._SrcSql1 = value;
            }
        }

        private string _SrcSql2;
        /// <summary></summary>
        public string SrcSql2
        {
            get
            {
                return this._SrcSql2;
            }
            set
            {
                this._SrcSql2 = value;
            }
        }

        private string _SrcKeyField1;
        /// <summary></summary>
        public string SrcKeyField1
        {
            get
            {
                return this._SrcKeyField1;
            }
            set
            {
                this._SrcKeyField1 = value;
            }
        }

        private string _SrcKeyField2;
        /// <summary></summary>
        public string SrcKeyField2
        {
            get
            {
                return this._SrcKeyField2;
            }
            set
            {
                this._SrcKeyField2 = value;
            }
        }

        private string _SrcField1;
        /// <summary></summary>
        public string SrcField1
        {
            get
            {
                return this._SrcField1;
            }
            set
            {
                this._SrcField1 = value;
            }
        }

        private string _SrcField2;
        /// <summary></summary>
        public string SrcField2
        {
            get
            {
                return this._SrcField2;
            }
            set
            {
                this._SrcField2 = value;
            }
        }

        private string _ComparerAssemblyName;
        /// <summary></summary>
        public string ComparerAssemblyName
        {
            get
            {
                return this._ComparerAssemblyName;
            }
            set
            {
                this._ComparerAssemblyName = value;
            }
        }

        private string _ComparerTypeName;
        /// <summary></summary>
        public string ComparerTypeName
        {
            get
            {
                return this._ComparerTypeName;
            }
            set
            {
                this._ComparerTypeName = value;
            }
        }

        private double _FieldSimilarityScoreThreshold;
        /// <summary></summary>
        public double FieldSimilarityScoreThreshold
        {
            get
            {
                return this._FieldSimilarityScoreThreshold;
            }
            set
            {
                this._FieldSimilarityScoreThreshold = value;
            }
        }

        private string _TgtTableName;
        /// <summary></summary>
        public string TgtTableName
        {
            get
            {
                return this._TgtTableName;
            }
            set
            {
                this._TgtTableName = value;
            }
        }

        private Dictionary<string,string> _Src1TgtFieldMappings;
        /// <summary></summary>
        public Dictionary<string,string> Src1TgtFieldMappings
        {
            get
            {
                return this._Src1TgtFieldMappings;
            }
            set
            {
                this._Src1TgtFieldMappings = value;
            }
        }

        private Dictionary<string,string> _Src2TgtFieldMappings;
        /// <summary></summary>
        public Dictionary<string,string> Src2TgtFieldMappings
        {
            get
            {
                return this._Src2TgtFieldMappings;
            }
            set
            {
                this._Src2TgtFieldMappings = value;
            }
        }

        private Dictionary<string,string> _Src1_Src2_Joinings;
        /// <summary></summary>
        public Dictionary<string,string> Src1_Src2_Joinings
        {
            get
            {
                return this._Src1_Src2_Joinings;
            }
            set
            {
                this._Src1_Src2_Joinings = value;
            }
        }

        private string _LinkScoreFieldInTargetTable;
        /// <summary></summary>
        public string LinkScoreFieldInTargetTable
        {
            get
            {
                return this._LinkScoreFieldInTargetTable;
            }
            set
            {
                this._LinkScoreFieldInTargetTable = value;
            }
        }

        private bool _SkipUpdateExistingData;
        /// <summary></summary>
        public bool SkipUpdateExistingData
        {
            get
            {
                return this._SkipUpdateExistingData;
            }
            set
            {
                this._SkipUpdateExistingData = value;
            }
        }
        #endregion

        #region ctor
        /// <summary>
        /// 
        /// </summary>
        public FuzzyLinkInstruction():base(Guid.Empty,Guid.Empty)
        {
            this._SrcDbProvider1 = DataProviderType.MSSQL;
            this._SrcDbProvider2 = DataProviderType.MSSQL;
            this._TgtDbProvider = DataProviderType.MSSQL;
            this._SrcConnStr1 = string.Empty;
            this._SrcConnStr2 = string.Empty;
            this._TgtConnStr = string.Empty;
            this._TgtTableName = string.Empty;
            this._SrcKeyField1 = string.Empty;
            this._SrcKeyField2 = string.Empty;
            this._SrcField1 = string.Empty;
            this._SrcField2 = string.Empty;
            this._Src1TgtFieldMappings = new Dictionary<string, string>();
            this._Src2TgtFieldMappings = new Dictionary<string, string>();
            this._ComparerAssemblyName = string.Empty;
            this._ComparerTypeName = string.Empty;
            this._FieldSimilarityScoreThreshold = 1.0;
            this._Src1_Src2_Joinings = new Dictionary<string, string>();
            this._LinkScoreFieldInTargetTable = string.Empty;
            this._SkipUpdateExistingData = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="componentID"></param>
        /// <param name="jobTicketID"></param>
        /// <param name="srcDbProvider1"></param>
        /// <param name="srcConnStr1"></param>
        /// <param name="srcSql1"></param>
        /// <param name="srcKey1"></param>
        /// <param name="srcField1"></param>
        /// <param name="srcDbProvider2"></param>
        /// <param name="srcConnStr2"></param>
        /// <param name="srcSql2"></param>
        /// <param name="srcKey2"></param>
        /// <param name="srcField2"></param>
        /// <param name="tgtDbProvider"></param>
        /// <param name="tgtConnStr"></param>
        /// <param name="tgtTableName"></param>
        /// <param name="src1Mappings"></param>
        /// <param name="src2Mappings"></param>
        /// <param name="comparerAssemblyFileName"></param>
        /// <param name="comparerTypeName"></param>
        /// <param name="similarityThreshold"></param>
        /// <param name="linkScoreField"></param>
        /// <param name="src1Src2Joinings"></param>
        /// <param name="skipUpdateExistingData"></param>
        public FuzzyLinkInstruction(Guid componentID, Guid jobTicketID, 
            DataProviderType srcDbProvider1,
            string srcConnStr1,
            string srcSql1,
            string srcKey1,
            string srcField1,
            DataProviderType srcDbProvider2,
            string srcConnStr2,
            string srcSql2,
            string srcKey2,
            string srcField2,
            DataProviderType tgtDbProvider,
            string tgtConnStr,
            string tgtTableName,
            Dictionary<string,string> src1Mappings,
            Dictionary<string,string> src2Mappings,
            string comparerAssemblyFileName,
            string comparerTypeName,
            double similarityThreshold,
            Dictionary<string,string> src1Src2Joinings,
            string linkScoreField, 
            bool skipUpdateExistingData) 
            : base(componentID, jobTicketID)
        {
            this._SrcDbProvider1 = srcDbProvider1;
            this._SrcConnStr1 = srcConnStr1;
            this._SrcSql1 = srcSql1;
            this._SrcKeyField1 = srcKey1;
            this._SrcField1 = srcField1;

            this._SrcDbProvider2 = srcDbProvider2;
            this._SrcConnStr2 = srcConnStr2;
            this._SrcSql2 = srcSql2;
            this._SrcKeyField2 = srcKey2;
            this._SrcField2 = srcField2;

            this._TgtDbProvider = tgtDbProvider;
            this._TgtConnStr = tgtConnStr;
            this._TgtTableName = tgtTableName;
            this._Src1TgtFieldMappings = src1Mappings;
            this._Src2TgtFieldMappings = src2Mappings;

            this._ComparerAssemblyName = comparerAssemblyFileName;
            this._ComparerTypeName = comparerTypeName;
            this._FieldSimilarityScoreThreshold = similarityThreshold;

            this._Src1_Src2_Joinings = src1Src2Joinings;
            this._LinkScoreFieldInTargetTable = linkScoreField;

            this._SkipUpdateExistingData = skipUpdateExistingData;
        }
        #endregion

        public override InstructionBase Instantiate(XmlNode xNode)
        {
            if (xNode.Attributes.GetNamedItem("Type").Value == this.GetType().FullName)
            {
                FuzzyLinkInstruction instruction = new FuzzyLinkInstruction();
                instruction.OwnerComponentID = new Guid(xNode.Attributes.GetNamedItem("ComponentID").Value);
                instruction.OwnerWorkerInstanceID = new Guid(xNode.Attributes.GetNamedItem("WorkerID").Value);
                instruction.SubmitTime = DateTime.Parse(xNode.Attributes.GetNamedItem("SubmitTime").Value);
                instruction.StartTime = DateTime.Parse(xNode.Attributes.GetNamedItem("StartTime").Value);

                instruction.SrcDbProvider1 =
                    (DataProviderType)
                    int.Parse(XmlDataUtil.GetAttributeValue(
                                  xNode, "SrcDbProvider1",
                                  ((int) DataProviderType.MSSQL).ToString()));
                instruction.SrcConnStr1 = XmlDataUtil.GetAttributeValue(xNode, "SrcConnStr1", "");
                instruction.SrcSql1 = XmlDataUtil.GetAttributeValue(xNode, "SrcSql1", "");
                instruction.SrcKeyField1 = XmlDataUtil.GetAttributeValue(xNode, "SrcKeyField1", "");
                instruction.SrcField1 = XmlDataUtil.GetAttributeValue(xNode, "SrcField1", "");

                instruction.SrcDbProvider2 =
                    (DataProviderType) int.Parse(
                                           XmlDataUtil.GetAttributeValue(
                                               xNode, "SrcDbProvider2", ((int) DataProviderType.MSSQL).ToString()));
                instruction.SrcConnStr2 = XmlDataUtil.GetAttributeValue(xNode, "SrcConnStr2", "");
                instruction.SrcSql2 = XmlDataUtil.GetAttributeValue(xNode, "SrcSql2", "");
                instruction.SrcKeyField2 = XmlDataUtil.GetAttributeValue(xNode, "SrcKeyField2", "");
                instruction.SrcField2 = XmlDataUtil.GetAttributeValue(xNode, "SrcField2", "");

                instruction.TgtDbProvider =
                    (DataProviderType) int.Parse(
                                           XmlDataUtil.GetAttributeValue(xNode, "TgtDbProvider",
                                                                         ((int) DataProviderType.MSSQL).ToString()));
                instruction.TgtConnStr = XmlDataUtil.GetAttributeValue(xNode, "TgtConnStr", "");
                instruction.TgtTableName = XmlDataUtil.GetAttributeValue(xNode, "TgtTableName", "");

                instruction.ComparerAssemblyName = XmlDataUtil.GetAttributeValue(xNode, "ComparerAssemblyName", "");
                instruction.ComparerTypeName = XmlDataUtil.GetAttributeValue(xNode, "ComparerTypeName", "");
                instruction.FieldSimilarityScoreThreshold =
                    double.Parse(XmlDataUtil.GetAttributeValue(xNode, "SimilarityThreshold", "1.0"));

                XmlNodeList src1MappingNodes = xNode.SelectNodes("Src1TgtFieldMappings/FieldMapping");
                if(src1MappingNodes !=null && src1MappingNodes.Count>0)
                {
                    instruction.Src1TgtFieldMappings = new Dictionary<string, string>();
                    foreach(XmlNode mappingNode in src1MappingNodes)
                    {
                        string srcField = XmlDataUtil.GetAttributeValue(mappingNode, "SrcField", "");
                        string tgtField = XmlDataUtil.GetAttributeValue(mappingNode, "TgtField", "");
                        instruction.Src1TgtFieldMappings.Add(srcField, tgtField);
                    }
                }

                XmlNodeList src2MappingNodes = xNode.SelectNodes("Src2TgtFieldMappings/FieldMapping");
                if(src2MappingNodes !=null && src2MappingNodes.Count>0)
                {
                    instruction.Src2TgtFieldMappings = new Dictionary<string, string>();
                    foreach(XmlNode mappingNode in src2MappingNodes)
                    {
                        string srcField = XmlDataUtil.GetAttributeValue(mappingNode, "SrcField", "");
                        string tgtField = XmlDataUtil.GetAttributeValue(mappingNode, "TgtField", "");
                        instruction.Src2TgtFieldMappings.Add(srcField, tgtField);
                    }
                }

                XmlNodeList srcFieldJoiningNodes = xNode.SelectNodes("SrcJoinings/SrcJoining");
                if(srcFieldJoiningNodes !=null && srcFieldJoiningNodes.Count>0)
                {
                    instruction.Src1_Src2_Joinings = new Dictionary<string, string>();
                    foreach(XmlNode joiningNode in srcFieldJoiningNodes)
                    {
                        string src1Field = XmlDataUtil.GetAttributeValue(joiningNode, "SrcField1", "");
                        string src2Field = XmlDataUtil.GetAttributeValue(joiningNode, "SrcField2", "");
                        instruction.Src1_Src2_Joinings.Add(src1Field, src2Field);
                    }
                }

                instruction.LinkScoreFieldInTargetTable = XmlDataUtil.GetAttributeValue(xNode, "LinkScoreField", "");
                instruction.SkipUpdateExistingData =
                    bool.Parse(XmlDataUtil.GetAttributeValue(xNode, "SkipUpdateExistingData", true.ToString()));

                return instruction;
            }
            else
            {
                throw new Exception("Unable to instantiate instruction: invalid type specified in xml node");
            }
        }

        public override XmlNode Serialize(ref XmlDocument xDoc, ref XmlNode parentNode)
        {
            XmlNode xNode = XmlDataUtil.AddElement(ref xDoc, parentNode, "Instruction");
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "Type", this.GetType().FullName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComponentID", this.OwnerComponentID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "WorkerID", this.OwnerWorkerInstanceID.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SubmitTime", this.SubmitTime.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "StartTime", this.StartTime.ToString());

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcDbProvider1", ((int) this.SrcDbProvider1).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcConnStr1", this.SrcConnStr1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcSql1", this.SrcSql1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcKeyField1", this.SrcKeyField1);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcField1", this.SrcField1);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcDbProvider2", ((int) this.SrcDbProvider2).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcConnStr2", this.SrcConnStr2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcSql2", this.SrcSql2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcKeyField2", this.SrcKeyField2);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SrcField2", this.SrcField2);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtDbProvider", ((int) this.TgtDbProvider).ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtConnStr", this.TgtConnStr);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "TgtTableName", this.TgtTableName);

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComparerAssemblyName", this.ComparerAssemblyName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "ComparerTypeName", this.ComparerTypeName);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SimilarityThreshold",
                                        this.FieldSimilarityScoreThreshold.ToString());

            if(this.Src1TgtFieldMappings !=null && this.Src1TgtFieldMappings.Count>0)
            {
                XmlNode mappingRootNode1 = XmlDataUtil.AddElement(ref xDoc, xNode, "Src1TgtFieldMappings");
                foreach(string srcFieldName in this.Src1TgtFieldMappings.Keys)
                {
                    XmlNode mappingNode = XmlDataUtil.AddElement(ref xDoc, mappingRootNode1, "FieldMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode, "SrcField", srcFieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode, "TgtField",
                                                this.Src1TgtFieldMappings[srcFieldName]);
                }
            }

            if(this.Src2TgtFieldMappings !=null && this.Src2TgtFieldMappings.Count>0)
            {
                XmlNode mappingRoot2 = XmlDataUtil.AddElement(ref xDoc, xNode, "Src2TgtFieldMappings");
                foreach(string srcFieldName in this.Src2TgtFieldMappings.Keys)
                {
                    XmlNode mappingNode = XmlDataUtil.AddElement(ref xDoc, mappingRoot2, "FieldMapping");
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode, "SrcField", srcFieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, mappingNode, "TgtField",
                                                this.Src2TgtFieldMappings[srcFieldName]);
                }
            }

            if(this.Src1_Src2_Joinings !=null && this.Src1_Src2_Joinings.Count>0)
            {
                XmlNode joinRootNode = XmlDataUtil.AddElement(ref xDoc, xNode, "SrcJoinings");
                foreach(string joinField1 in this.Src1_Src2_Joinings.Keys)
                {
                    XmlNode joinNode = XmlDataUtil.AddElement(ref xDoc, joinRootNode, "SrcJoining");
                    XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "SrcField1", joinField1);
                    XmlDataUtil.UpdateAttribute(ref xDoc, joinNode, "SrcField2", this.Src1_Src2_Joinings[joinField1]);
                }
            }

            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "LinkScoreField", this.LinkScoreFieldInTargetTable);
            XmlDataUtil.UpdateAttribute(ref xDoc, xNode, "SkipUpdateExistingData",
                                        this.SkipUpdateExistingData.ToString());
            
            return xNode;
        }
    }
}
